﻿// http://tranxcoder.wordpress.com/2008/10/12/customizing-lookful-wpf-controls-take-2/

using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Documents;

namespace ProjectEuler.UI
{
    public class TextBlockService
    {
        private static readonly DependencyPropertyKey IsTextTrimmedKey;

        public static readonly ResourceKey AutoToolTipStyleKey;
        public static readonly DependencyProperty AutoToolTipEnabledProperty;
        public static readonly DependencyProperty BoundTextProperty;
        public static readonly DependencyProperty IsTextTrimmedProperty;

        static TextBlockService()
        {
            AutoToolTipStyleKey = new ComponentResourceKey(typeof(TextBlockService), new Object());

            AutoToolTipEnabledProperty = DependencyProperty.RegisterAttached(
                "AutoToolTipEnabled",
                typeof(Boolean),
                typeof(TextBlockService),
                new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.Inherits));

            BoundTextProperty = DependencyProperty.RegisterAttached(
                "BoundText",
                typeof(String),
                typeof(TextBlockService),
                new FrameworkPropertyMetadata(null, OnBoundTextChanged));

            IsTextTrimmedKey = DependencyProperty.RegisterAttachedReadOnly(
                "IsTextTrimmed",
                typeof(Boolean),
                typeof(TextBlockService),
                new FrameworkPropertyMetadata(false));

            IsTextTrimmedProperty = IsTextTrimmedKey.DependencyProperty;

            EventManager.RegisterClassHandler(
                typeof(TextBlock),
                FrameworkElement.SizeChangedEvent,
                new SizeChangedEventHandler(OnTextBlockSizeChanged),
                true);
        }

        [AttachedPropertyBrowsableForType(typeof(DependencyObject))]
        public static Boolean GetAutoToolTipEnabled(DependencyObject element)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }

            return (Boolean)element.GetValue(AutoToolTipEnabledProperty);
        }

        [AttachedPropertyBrowsableForType(typeof(Inline))]
        public static String GetBoundText(Inline inline)
        {
            if (inline == null)
            {
                throw new ArgumentNullException("inline");
            }

            return (String)inline.GetValue(BoundTextProperty);
        }

        [AttachedPropertyBrowsableForType(typeof(TextBlock))]
        public static Boolean GetIsTextTrimmed(TextBlock target)
        {
            return (Boolean)target.GetValue(IsTextTrimmedProperty);
        }

        public static void SetAutoToolTipEnabled(DependencyObject element, Boolean value)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }

            element.SetValue(AutoToolTipEnabledProperty, value);
        }

        public static void SetBoundText(Inline inline, String value)
        {
            if (inline == null)
            {
                throw new ArgumentNullException("inline");
            }

            inline.SetValue(BoundTextProperty, value);
        }

        private static void SetIsTextTrimmed(TextBlock target, Boolean value)
        {
            target.SetValue(IsTextTrimmedKey, value);
        }

        private static void OnBoundTextChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var str = (e.NewValue as String);
            
            var run = (sender as Run);
            var span = (sender as Span);

            if (run != null)
            {
                run.Text = (String)(e.NewValue);
            }

            if (span != null)
            {
                span.Inlines.Clear();
                span.Inlines.Add(str);
            }
        }

        private static void OnTextBlockSizeChanged(Object sender, SizeChangedEventArgs e)
        {
            var textBlock = sender as TextBlock;

            if (textBlock != null)
            {
                if (textBlock.TextTrimming == TextTrimming.None)
                {
                    SetIsTextTrimmed(textBlock, false);
                }
                else
                {
                    SetIsTextTrimmed(textBlock, CalculateIsTextTrimmed(textBlock));
                }
            }
        }

        private static Boolean CalculateIsTextTrimmed(TextBlock textBlock)
        {
            if (textBlock.IsArrangeValid)
            {
                return CreateFormattedText(textBlock).Height > textBlock.ActualHeight;
            }

            return GetIsTextTrimmed(textBlock);
        }

        private static FormattedText CreateFormattedText(TextBlock block)
        {
            return new FormattedText(
                block.Text,
                CultureInfo.CurrentCulture,
                block.FlowDirection,
                new Typeface(block.FontFamily, block.FontStyle, block.FontWeight, block.FontStretch),
                block.FontSize,
                block.Foreground)
            {
                MaxTextWidth = block.ActualWidth
            };
        }
    }
}
